#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>

/*
mykeys{
    interrupt-parent = <&gpiof>;
    interrupts = <9 0>, <7 0>, <8 0>;
    interrupts-name = "key1", "key2", "key3";
    key-gpios = <&gpiof 9 0>, <&gpiof 7 0>, <&gpiof 8 0>
}
*/

MODULE_LICENSE("GPL");

typedef struct 
{
    unsigned int m_irqNum;
    const char* m_btnName;
    struct timer_list m_timer;
    struct gpio_desc* m_pinDesc;
}btn_desc_t;

btn_desc_t btns[3];

struct device_node* btnNode;

void btn_timer_handle_func(struct timer_list* t)
{
    int state = 0;
    btn_desc_t* data = container_of(t,btn_desc_t,m_timer);

    state = gpiod_get_value(data->m_pinDesc);
    printk("%s is %s!\n", data->m_btnName, state?"release": "pressed");
}

irqreturn_t btn_handle(int irqNum,void* dev)
{
    btn_desc_t* data = (btn_desc_t*)dev;

    data->m_timer.expires = jiffies + (HZ / 100);
    mod_timer(&data->m_timer,jiffies + (HZ / 100));

    return IRQ_HANDLED;
}

static int __init btn_drv_init(void)
{
    int i = 0;

    btnNode = of_find_node_by_path("/mykeys");

    for(;i<ARRAY_SIZE(btns);i++)
    {
        btns[i].m_irqNum = irq_of_parse_and_map(btnNode,i);
        of_property_read_string_index(btnNode,"interrupts-name",i,&btns[i].m_btnName);

        request_irq(btns[i].m_irqNum, btn_handle,
            IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, btns[i].m_btnName, &btns[i]);

        btns[i].m_pinDesc = gpiod_get_from_of_node(btnNode,"key-gpios",i,GPIOD_IN,btns[i].m_btnName);

        timer_setup(&btns[i].m_timer,btn_timer_handle_func,0);
    }
    return 0;
}

static void __exit btn_drv_exit(void)
{
    int i = 0;
    for(;i<ARRAY_SIZE(btns);i++)
    {
        gpiod_put(btns[i].m_pinDesc);
        free_irq(btns[i].m_irqNum,&btns[i]);
    }
}

module_init(btn_drv_init);
module_exit(btn_drv_exit);